import { type FormEvent, useRef } from 'react';
import useToast from 'hooks/useToast';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
  clientUpdateUserPassword,
  queryPasswordPublicKey,
} from '@/services/api';
import type JSEncrypt from 'jsencrypt';
import type { TBody } from '@/types';
import { useTranslations } from 'use-intl';

export default function UpdatePasswordUserIdPage() {
  const updatePasswordInputRef = useRef<HTMLInputElement>(null);
  const jsEncryptRef = useRef<JSEncrypt | null>(null);
  const { show } = useToast();
  const t = useTranslations('UserIdPage');

  const queryPasswordPublicKeyQuery = useQuery(
    ['/key', '/public', '/password'],
    async () => {
      return (await queryPasswordPublicKey()) as string;
    },
    {
      enabled: false,
    },
  );

  const clientUpdateUserPasswordMutation = useMutation(
    async (
      variables: TBody<{
        password?: string;
      }>,
    ) => {
      await clientUpdateUserPassword(variables);
    },
  );

  async function onSubmitUpdatePassword(e: FormEvent<HTMLFormElement>) {
    try {
      e.stopPropagation();
      e.preventDefault();

      const current = updatePasswordInputRef.current;
      if (!current) {
        show({
          type: 'DANGER',
          message: t('updatePasswordFailNoData'),
        });
        return;
      }

      const value = current.value.trim();
      if (!value) {
        current.focus();
        show({
          type: 'DANGER',
          message: t('passwordCannotBeEmpty'),
        });
        return;
      }

      await clientUpdateUserPasswordMutation.mutateAsync({
        data: {
          password: await getEncryptedPassword(value),
        },
      });

      current.value = '';
      show({
        type: 'SUCCESS',
        message: t('updatePasswordCompleted'),
      });
    } catch (e) {
      clientUpdateUserPasswordMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  async function getJsEncrypt(): Promise<JSEncrypt> {
    let jsEncrypt;
    if (jsEncryptRef.current) {
      jsEncrypt = jsEncryptRef.current;
    } else {
      const JSEncrypt = (await import('jsencrypt')).JSEncrypt;
      jsEncrypt = new JSEncrypt();
      jsEncryptRef.current = jsEncrypt;
    }
    return jsEncrypt;
  }

  async function getEncryptedPassword(password: string): Promise<string> {
    const jsEncrypt = await getJsEncrypt();
    const result = await queryPasswordPublicKeyQuery.refetch({
      throwOnError: true,
    });
    const publicKey = result.data;
    if (!publicKey) {
      throw t('getPasswordDataFail');
    }

    jsEncrypt.setPublicKey(publicKey);
    const encryptedData = jsEncrypt.encrypt(password);
    if (!encryptedData) {
      throw t('getPasswordDataFail');
    }

    return encryptedData;
  }

  return (
    <form className="vstack gap-4" onSubmit={onSubmitUpdatePassword}>
      <input
        readOnly
        hidden
        name="username"
        type="text"
        className="form-control"
        autoComplete="username"
      />
      <input
        name="password"
        ref={updatePasswordInputRef}
        type="password"
        className="form-control"
        placeholder={t('enterNewPassword')}
        autoComplete="new-password"
      />
      <button
        disabled={clientUpdateUserPasswordMutation.isLoading}
        type="submit"
        className="btn btn-outline-success"
      >
        {clientUpdateUserPasswordMutation.isLoading ? (
          <span
            className="spinner-border spinner-border-sm me-2"
            role="status"
            aria-hidden="true"
          ></span>
        ) : (
          <i className="bi bi-save me-2"></i>
        )}
        {t('updatePassword')}
      </button>
    </form>
  );
}
